﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Net;
using System.Text;
using Newtonsoft.Json;

namespace Eggplant.Web.Rest.Handlers
{
	public abstract class AuthenticatingHandler : IHttpHandler
	{
		public bool IsReusable
		{
			get { return false; }
		}

		public HttpContext Context
		{
			get;
			private set;
		}

		public SessionInfo SessionInfo
		{
			get;
			private set;
		}

		public virtual bool IsAuthenticationEnabled
		{
			get { return true; }
		}

		public virtual string SessionTokenHeaderName
		{
			get { return "x-eggplant-session"; }
		}

		public virtual string UserTokenHeaderName
		{
			get { return "x-eggplant-user"; }
		}

		public virtual string AuthTokenHeaderName
		{
			get { return "x-eggplant-auth"; }
		}

		public virtual string StartSessionUrl
		{
			get { return "/login"; }
		}

		public virtual string StartSessionHttpMethod
		{
			get { return "POST"; }
		}

		protected virtual void ProcessRequest()
		{
		}

		public void ProcessRequest(HttpContext context)
		{
			this.Context = context;

			if (IsAuthenticationEnabled)
			{
				string userToken = context.Request.Headers[this.UserTokenHeaderName];
				if (String.IsNullOrWhiteSpace(userToken))
					throw new HttpException("Invalid authentication details.", (int)HttpStatusCode.Forbidden);

				string sessionToken;

				// Check if this is a validation request
				if (context.Request.AppRelativeCurrentExecutionFilePath.Replace("~", string.Empty).ToLower() == this.StartSessionUrl &&
					context.Request.HttpMethod == StartSessionHttpMethod)
				{
					string authToken = context.Request.Headers[this.AuthTokenHeaderName];
					if (String.IsNullOrWhiteSpace(authToken) || (sessionToken = StartSession(userToken, authToken)) == null)
						throw new HttpException("Invalid authentication details.", (int)HttpStatusCode.Forbidden);
					else
					{
						// Return headers and exit
						context.Response.Headers[UserTokenHeaderName] = userToken;
						context.Response.Headers[SessionTokenHeaderName] = sessionToken;
						HttpManager.SetResponse(context, HttpStatusCode.OK);
						return;
					}
				}
				else
				{
					sessionToken = context.Request.Headers[this.SessionTokenHeaderName];
					if (String.IsNullOrWhiteSpace(sessionToken) || !AuthenticateSession(userToken, sessionToken))
						throw new HttpException("You do not have access to this resource.", (int)HttpStatusCode.Forbidden);
				}

				this.SessionInfo = new SessionInfo() { UserToken = userToken, SessionToken = sessionToken };
			}

			this.ProcessRequest();
		}

		/// <summary>
		/// Checks that the provided user/session token pair in valid.
		/// </summary>
		/// <param name="userToken"></param>
		/// <param name="sessionToken"></param>
		/// <returns>True if this is a valid session, false if not.</returns>
		protected virtual bool AuthenticateSession(string userToken, string sessionToken)
		{
			throw new NotImplementedException("If IsAuthenticationEnabled is true, AuthenticateSession must be implemented.");
		}

		/// <summary>
		/// Starts a new session if the user/authentication token pair is valid.
		/// </summary>
		/// <param name="userToken"></param>
		/// <param name="authToken"></param>
		/// <returns>A new session token that will be used for making requests, or null of this is an invalid request.</returns>
		protected virtual string StartSession(string userToken, string authToken)
		{
			throw new NotImplementedException("If IsAuthenticationEnabled is true, StartSession must be implemented.");
		}
	}

	public class SessionInfo
	{
		public string UserToken { get; internal set; }
		public string SessionToken { get; internal set; }
	}
}